{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "9TV7IYeqifSv"
   },
   "source": [
    "##### Copyright 2018 The TensorFlow Authors. [Licensed under the Apache License, Version 2.0](#scrollTo=ByZjmtFgB_Y5)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "tRIJp_4m_Afz"
   },
   "outputs": [],
   "source": [
    "// #@title Licensed under the Apache License, Version 2.0 (the \"License\"); { display-mode: \"form\" }\n",
    "// Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "// you may not use this file except in compliance with the License.\n",
    "// You may obtain a copy of the License at\n",
    "//\n",
    "// https://www.apache.org/licenses/LICENSE-2.0\n",
    "//\n",
    "// Unless required by applicable law or agreed to in writing, software\n",
    "// distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "// See the License for the specific language governing permissions and\n",
    "// limitations under the License."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "sI1ZtrdiA4aY"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    " <td>\n",
    "  <a target=\"_blank\" href=\"https://www.tensorflow.org/swift/tutorials/python_interoperability\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />View on TensorFlow.org</a>\n",
    " </td>\n",
    " <td>\n",
    "  <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/swift/blob/master/docs/site/tutorials/python_interoperability.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Run in Google Colab</a>\n",
    " </td>\n",
    " <td>\n",
    "  <a target=\"_blank\" href=\"https://github.com/tensorflow/swift/blob/master/docs/site/tutorials/python_interoperability.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />View source on GitHub</a>\n",
    " </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "8sa42_NblqRE"
   },
   "source": [
    "# Python interoperability\n",
    "\n",
    "Swift For TensorFlow supports Python interoperability.\n",
    "\n",
    "You can import Python modules from Swift, call Python functions, and convert values between Swift and Python."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "kZRlD4utdPuX"
   },
   "outputs": [],
   "source": [
    "// comment so that Colab does not interpret `#if ...` as a comment\n",
    "#if canImport(PythonKit)\n",
    "    import PythonKit\n",
    "#else\n",
    "    import Python\n",
    "#endif\n",
    "print(Python.version)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "W7MpNcIwIIy8"
   },
   "source": [
    "## Setting the Python version"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lM9dRji7IIy8"
   },
   "source": [
    "By default, when you `import Python`, Swift searches system library paths for the newest version of Python installed. \n",
    "To use a specific Python installation, set the `PYTHON_LIBRARY` environment variable to the `libpython` shared library provided by the installation. For example: \n",
    "\n",
    "`export PYTHON_LIBRARY=\"~/anaconda3/lib/libpython3.7m.so\"`\n",
    "\n",
    "The exact filename will differ across Python environments and platforms."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "eoyLeSQVIIy9"
   },
   "source": [
    "Alternatively, you can set the `PYTHON_VERSION` environment variable, which instructs Swift to search system library paths for a matching Python version. Note that `PYTHON_LIBRARY` takes precedence over `PYTHON_VERSION`.\n",
    "\n",
    "In code, you can also call the `PythonLibrary.useVersion` function, which is equivalent to setting `PYTHON_VERSION`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "FCMWR11NIIy-"
   },
   "outputs": [],
   "source": [
    "// PythonLibrary.useVersion(2)\n",
    "// PythonLibrary.useVersion(3, 7)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "HrlMNOinIIy_"
   },
   "source": [
    "__Note: you should run `PythonLibrary.useVersion` right after `import Python`, before calling any Python code. It cannot be used to dynamically switch Python versions.__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "mIbIOW0HIIzA"
   },
   "source": [
    "Set `PYTHON_LOADER_LOGGING=1` to see [debug output for Python library loading](https://github.com/apple/swift/pull/20674#discussion_r235207008). "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "rU0WY_sJodio"
   },
   "source": [
    "## Basics\n",
    "\n",
    "In Swift, `PythonObject` represents an object from Python.\n",
    "All Python APIs use and return `PythonObject` instances.\n",
    "\n",
    "Basic types in Swift (like numbers and arrays) are convertible to `PythonObject`. In some cases (for literals and functions taking `PythonConvertible` arguments), conversion happens implicitly. To explicitly cast a Swift value to `PythonObject`, use the `PythonObject` initializer.\n",
    "\n",
    "`PythonObject` defines many standard operations, including numeric operations, indexing, and iteration."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "kqXILiXhq-iM"
   },
   "outputs": [],
   "source": [
    "// Convert standard Swift types to Python.\n",
    "let pythonInt: PythonObject = 1\n",
    "let pythonFloat: PythonObject = 3.0\n",
    "let pythonString: PythonObject = \"Hello Python!\"\n",
    "let pythonRange: PythonObject = PythonObject(5..<10)\n",
    "let pythonArray: PythonObject = [1, 2, 3, 4]\n",
    "let pythonDict: PythonObject = [\"foo\": [0], \"bar\": [1, 2, 3]]\n",
    "\n",
    "// Perform standard operations on Python objects.\n",
    "print(pythonInt + pythonFloat)\n",
    "print(pythonString[0..<6])\n",
    "print(pythonRange)\n",
    "print(pythonArray[2])\n",
    "print(pythonDict[\"bar\"])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "fEAEyUExXT3I"
   },
   "outputs": [],
   "source": [
    "// Convert Python objects back to Swift.\n",
    "let int = Int(pythonInt)!\n",
    "let float = Float(pythonFloat)!\n",
    "let string = String(pythonString)!\n",
    "let range = Range<Int>(pythonRange)!\n",
    "let array: [Int] = Array(pythonArray)!\n",
    "let dict: [String: [Int]] = Dictionary(pythonDict)!\n",
    "\n",
    "// Perform standard operations.\n",
    "// Outputs are the same as Python!\n",
    "print(Float(int) + float)\n",
    "print(string.prefix(6))\n",
    "print(range)\n",
    "print(array[2])\n",
    "print(dict[\"bar\"]!)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "1pMewsl0VgnJ"
   },
   "source": [
    "`PythonObject` defines conformances to many standard Swift protocols:\n",
    "* `Equatable`\n",
    "* `Comparable`\n",
    "* `Hashable`\n",
    "* `SignedNumeric`\n",
    "* `Strideable`\n",
    "* `MutableCollection`\n",
    "* All of the `ExpressibleBy_Literal` protocols\n",
    "\n",
    "Note that these conformances are not type-safe: crashes will occur if you attempt to use protocol functionality from an incompatible `PythonObject` instance."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "W9bUsiOxVf_v"
   },
   "outputs": [],
   "source": [
    "let one: PythonObject = 1\n",
    "print(one == one)\n",
    "print(one < one)\n",
    "print(one + one)\n",
    "\n",
    "let array: PythonObject = [1, 2, 3]\n",
    "for (i, x) in array.enumerated() {\n",
    "  print(i, x)\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "w3lmTRCWT5sS"
   },
   "source": [
    "To convert tuples from Python to Swift, you must statically know the arity of the tuple.\n",
    "\n",
    "Call one of the following instance methods:\n",
    "- `PythonObject.tuple2`\n",
    "- `PythonObject.tuple3`\n",
    "- `PythonObject.tuple4`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "fQ0HEX89T4mW"
   },
   "outputs": [],
   "source": [
    "let pythonTuple = Python.tuple([1, 2, 3])\n",
    "print(pythonTuple, Python.len(pythonTuple))\n",
    "\n",
    "// Convert to Swift.\n",
    "let tuple = pythonTuple.tuple3\n",
    "print(tuple)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Te7sNNx9c_am"
   },
   "source": [
    "## Python builtins\n",
    "\n",
    "Access Python builtins via the global `Python` interface."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "jpcOByipc75O"
   },
   "outputs": [],
   "source": [
    "// `Python.builtins` is a dictionary of all Python builtins.\n",
    "_ = Python.builtins\n",
    "\n",
    "// Try some Python builtins.\n",
    "print(Python.type(1))\n",
    "print(Python.len([1, 2, 3]))\n",
    "print(Python.sum([1, 2, 3]))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "H2wwUL1tY3JX"
   },
   "source": [
    "## Importing Python modules\n",
    "\n",
    "Use `Python.import` to import a Python module. It works like the `import` keyword in `Python`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "XrZee8n3Y17_"
   },
   "outputs": [],
   "source": [
    "let np = Python.import(\"numpy\")\n",
    "print(np)\n",
    "let zeros = np.ones([2, 3])\n",
    "print(zeros)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hQvza3dUXlr0"
   },
   "source": [
    "Use the throwing function `Python.attemptImport` to perform safe importing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "QD-uQGuaXhrM"
   },
   "outputs": [],
   "source": [
    "let maybeModule = try? Python.attemptImport(\"nonexistent_module\")\n",
    "print(maybeModule)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Qej_Z6V3mZnG"
   },
   "source": [
    "## Conversion with `numpy.ndarray`\n",
    "\n",
    "The following Swift types can be converted to and from `numpy.ndarray`:\n",
    "- `Array<Element>`\n",
    "- `ShapedArray<Scalar>`\n",
    "- `Tensor<Scalar>`\n",
    "\n",
    "Conversion succeeds only if the `dtype` of the `numpy.ndarray` is compatible with the `Element` or `Scalar` generic parameter type.\n",
    "\n",
    "For `Array`, conversion from `numpy` succeeds only if the `numpy.ndarray` is 1-D."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "hPvKgZBeDQ1p"
   },
   "outputs": [],
   "source": [
    "import TensorFlow\n",
    "\n",
    "let numpyArray = np.ones([4], dtype: np.float32)\n",
    "print(\"Swift type:\", type(of: numpyArray))\n",
    "print(\"Python type:\", Python.type(numpyArray))\n",
    "print(numpyArray.shape)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "ZuDgZ5cBS3Uk"
   },
   "outputs": [],
   "source": [
    "// Examples of converting `numpy.ndarray` to Swift types.\n",
    "let array: [Float] = Array(numpy: numpyArray)!\n",
    "let shapedArray = ShapedArray<Float>(numpy: numpyArray)!\n",
    "let tensor = Tensor<Float>(numpy: numpyArray)!\n",
    "\n",
    "// Examples of converting Swift types to `numpy.ndarray`.\n",
    "print(array.makeNumpyArray())\n",
    "print(shapedArray.makeNumpyArray())\n",
    "print(tensor.makeNumpyArray())\n",
    "\n",
    "// Examples with different dtypes.\n",
    "let doubleArray: [Double] = Array(numpy: np.ones([3], dtype: np.float))!\n",
    "let intTensor = Tensor<Int32>(numpy: np.ones([2, 3], dtype: np.int32))!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "8EQFZZ5iafwh"
   },
   "source": [
    "## Displaying images\n",
    "\n",
    "You can display images in-line using `matplotlib`, just like in Python notebooks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "vjQ7Rd3_IXuX"
   },
   "outputs": [],
   "source": [
    "// This cell is here to display plots inside a Jupyter Notebook.\n",
    "// Do not copy it into another environment.\n",
    "%include \"EnableIPythonDisplay.swift\"\n",
    "IPythonDisplay.shell.enable_matplotlib(\"inline\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 0,
   "metadata": {
    "colab": {},
    "colab_type": "code",
    "id": "jUzsa2cxafQV"
   },
   "outputs": [],
   "source": [
    "let np = Python.import(\"numpy\")\n",
    "let plt = Python.import(\"matplotlib.pyplot\")\n",
    "\n",
    "let time = np.arange(0, 10, 0.01)\n",
    "let amplitude = np.exp(-0.1 * time)\n",
    "let position = amplitude * np.sin(3 * time)\n",
    "\n",
    "plt.figure(figsize: [15, 10])\n",
    "\n",
    "plt.plot(time, position)\n",
    "plt.plot(time, amplitude)\n",
    "plt.plot(time, -amplitude)\n",
    "\n",
    "plt.xlabel(\"Time (s)\")\n",
    "plt.ylabel(\"Position (m)\")\n",
    "plt.title(\"Oscillations\")\n",
    "\n",
    "plt.show()"
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "name": "Python interoperability.ipynb",
   "provenance": [],
   "toc_visible": true
  },
  "kernelspec": {
   "display_name": "Swift",
   "language": "swift",
   "name": "swift"
  },
  "language_info": {
   "file_extension": ".swift",
   "mimetype": "text/x-swift",
   "name": "swift",
   "version": ""
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
